#include "GSP320.h"

GSP320::GSP320(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP)
: D3DApp(hInstance, winCaption, devType, requestedVP)
{
	//make sure the computer has the proper hardware for our settings
	if(!checkDeviceCaps())
	{
		MessageBox(0, "checkDeviceCaps() Failed", 0, 0);
		PostQuitMessage(0);
	}

	gameStart = false;

	mVB = NULL;
	mIB = NULL;

	//set up camera info
	mCameraRadius    = 25.0f;
	mCameraRotationY = 0.5 * D3DX_PI;
	mCameraHeight    = 20.0f;

	//this creates a new class for the font class (well what will become our font class)
	mGfxStats = new GfxStats();

	//obtain a pointer to a ID3DXSprite class, mSprite being the pointer
	HR(D3DXCreateSprite(gd3dDevice, &mSprite));

	//font declaration just cuz i didn't want a plain window
	D3DXFONT_DESC fontDesc;
		fontDesc.Height = 15;
		fontDesc.Width = 10;
		fontDesc.Weight = FW_BOLD;
		fontDesc.MipLevels = 0;
		fontDesc.Italic = true;
		fontDesc.CharSet = DEFAULT_CHARSET;
		fontDesc.OutputPrecision = OUT_DEFAULT_PRECIS;
		fontDesc.Quality = DEFAULT_QUALITY;
		fontDesc.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
		_tcscpy(fontDesc.FaceName, _T("Comic Sans"));

		HR(D3DXCreateFontIndirect(gd3dDevice, &fontDesc, &mFont));
	ballSetup();
	setupFX();
	//initialize our vertex declarationsm
	InitAllVertexDeclarations();
	//build vertex and index buffers for our ground grid
	buildBuffers();
	//we call this because we want the device states to be set during construction as well as when a device is reset
	onResetDevice();
}




//NOTE -- ball->ballPos().y is the ball's starting height.
//Students will need to alter this value to get different starting heights.
////////////////////////////////////////////////////////////////////////////////////////////////////////////
void GSP320::ballSetup()
{
	//set up ball
	ball = new Ball();

	//this will set the balls starting position
	ball->ballPos().x = 0.0f;
	ball->ballPos().y = 50.0f;
	ball->ballPos().z = 0.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////

GSP320::~GSP320()
{
	delete ball;
	delete mGfxStats;	
	ReleaseCOM(mFX);
	ReleaseCOM(mFont);
	ReleaseCOM(mSprite);

	if(mVB != NULL)
		ReleaseCOM(mVB);
	if(mIB != NULL)
		ReleaseCOM(mIB);
	delete mVB;
	delete mIB;
DestroyAllVertexDeclarations();

}

bool GSP320::checkDeviceCaps()
{
	//check for shader support
	D3DCAPS9 caps;
	HR(gd3dDevice->GetDeviceCaps(&caps));

	//check for vertex and pixel shader version 2.0 support
	if(caps.VertexShaderVersion < D3DVS_VERSION(2, 0))
	{
		MessageBox(0, "Your computer does not support Vertex Shader Version 2.0!", "Vertex Shader Support", 0);
		return false;
	}

	if(caps.PixelShaderVersion < D3DPS_VERSION(2, 0))
	{
		MessageBox(0, "Your computer does not support Pixel Shader Version 2.0!", "Pixel Shader Support", 0);
		return false;
	}
	return true;
}

void GSP320::onLostDevice()
{
	//this is where we put any code that needs to be executed before the rendering device is reset
	mGfxStats->onLostDevice();
	HR(mFont->OnLostDevice());
	gDInput->onLostDevice();
	HR(mSprite->OnLostDevice());
}

void GSP320::onResetDevice()
{	
	buildProjMtx();

	//call the onResetDevice of other objects
	mGfxStats->onResetDevice();
	HR(mSprite->OnResetDevice());
	HR(mFont->OnResetDevice());
}

void GSP320::updateScene(float dt)
{
	//this is somewhat how font is used, i will add a different function tho that is separate, but this
	//will give you a good idea if you look at the update function or display function in GfxStats
	//I will add another function to actually draw, but I wanted to try and get you a basic working bare bone
	//model so you could start looking around and see what is needed to get the window up adn then see what
	//is needed to actually add things such as sprites and what not

	//this function basically updates everything. It polls the keyboard, updates statistics and graphics, etc
	//two triangles for each sprite--two for background, two for ship, and two for each bullet.  Similarly, 4 vertices 
	//for each sprite.

	//notice how u update, then draw, order is important
	//mGfxStats->setTriCount(4 + mBulletList.size()*2);
	//mGfxStats->setVertexCount(8 + mBulletList.size()*4);
	//mGfxStats->update(dt);

	//Get snapshot of input devices
	gDInput->poll();	

	if(gDInput->keyDown(DIK_RETURN))
		gameStart = true;

	if(gameStart)
		ball->update(dt);	
	
	// The camera position/orientation relative to world space can 
	// change every frame based on input, so we need to rebuild the
	// view matrix every frame with the latest changes.
	buildViewMtx();
}

void GSP320::display()
{
	//Make static copy so memory is not allocated every frame
	static char buffer[256];

	sprintf(buffer, "Accumulated Time: %.2f seconds", ball->getAccumTime());
	
	RECT R = {5, 5, 0, 0};
	HR(mFont->DrawText(0, buffer, -1, &R, DT_NOCLIP, D3DCOLOR_XRGB(0, 0, 0)));
}
//this function is very important, although it will change greatly for our game
void GSP320::drawScene()
{

 //   // Now we can clear just view-port's portion of the buffer to red...
	HR(gd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0));

	HR(gd3dDevice->BeginScene());
    
    //setup rendering technique
	HR(mFX->SetTechnique(mhTech));

	//set effect parameters for initial use
	HR(mFX->SetMatrix(mhWVP, &(mView*mProj)));

	//begin pass
	UINT numPasses = 0;

	//begin fx rendering
	HR(mFX->Begin(&numPasses, 0));
	for(UINT i =0; i < numPasses; i++)
	{
		HR(mFX->BeginPass(i));
	//definition for translation maxtrix
	  D3DXMATRIX T;
	  D3DXMatrixTranslation(&T, ball->ballPos().x, ball->ballPos().y, ball->ballPos().z);
	  HR(mFX->SetMatrix(mhWVP, &(T*mView*mProj)));
	  HR(mFX->CommitChanges());
	 
	 
	 
	  ball->draw();
	  drawGrid();
	  HR(mFX->EndPass());
	}

	HR(mFX->End());

	display();

	HR(gd3dDevice->EndScene());



	//present the back buffer
	HR(gd3dDevice->Present(0, 0, 0, 0));

}

void GSP320::setupFX()
{
	mFX = 0;

	ID3DXBuffer* mFXErrors = 0;

	//create the effects object, if there are errors, display them
		//the following functions takes in the device to attach it too (our Direct3DDevice),
		//the name of the file to pull the effects from
		//defines & includes -- this shd be 0 for us
		//any flags (I want to know if we have errors so I set the Debug flag)
		//the next parameter specifies if we are sharing variables in the fx file with other fx files, we
		//aren't so it is 0
		//the last two parameters are the addresses of pointers to the fx object and the error buffer
	HR(D3DXCreateEffectFromFile(gd3dDevice, "GSP320.fx", 0, 0, D3DXSHADER_DEBUG, 0, &mFX, &mFXErrors));

	if( mFXErrors )
		MessageBox(0, (char*)mFXErrors->GetBufferPointer(), 0, 0);

	//get handles
	mhTech = mFX->GetTechniqueByName("TransformTech");
	mhWVP = mFX->GetParameterByName(0, "gWVP");
}	

//the following functions help set up the camera and the world view
void GSP320::buildViewMtx()
{
	float x = mCameraRadius * cosf(mCameraRotationY);
	float z = mCameraRadius * sinf(mCameraRotationY);
	D3DXVECTOR3 pos(x, mCameraHeight, z);
	D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	D3DXMatrixLookAtLH(&mView, &pos, &target, &up);
}

void GSP320::buildProjMtx()
{
	float w = WIDTH;
	float h = HEIGHT;
	D3DXMatrixPerspectiveFovLH(&mProj, D3DX_PI * 0.25f, w/h, 1.0f, 5000.0f);
}

void GSP320::drawGrid()
{
	// Let Direct3D know the vertex buffer, index buffer and vertex 
	// declaration we are using.
	HR(gd3dDevice->SetStreamSource(0, mVB, 0, sizeof(VertexPN)));
	HR(gd3dDevice->SetIndices(mIB));
	HR(gd3dDevice->SetVertexDeclaration(VertexPN::Decl));
	HR(gd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME));

	HR(mFX->SetMatrix(mhWVP, &(mView * mProj)));
	HR(mFX->CommitChanges());

	HR(gd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mNumVertices, 0, mNumTriangles));
	
}

void GSP320::generateGrid(int numVertRows, int numVertCols,
				float dx, float dz, 
				const D3DXVECTOR3& center, 
				std::vector<D3DXVECTOR3>& verts,
				std::vector<DWORD>& indices)
{
	int numVertices = numVertRows*numVertCols;
	int numCellRows = numVertRows-1;
	int numCellCols = numVertCols-1;

	int numTris = numCellRows*numCellCols*2;

	float width = (float)numCellCols * dx;
	float depth = (float)numCellRows * dz;

	//===========================================
	// Build vertices.

	// We first build the grid geometry centered about the origin and on
	// the xz-plane, row-by-row and in a top-down fashion.  We then translate
	// the grid vertices so that they are centered about the specified 
	// parameter 'center'.

	verts.resize( numVertices );

	// Offsets to translate grid from quadrant 4 to center of 
	// coordinate system.
	float xOffset = -width * 0.5f; 
	float zOffset =  depth * 0.5f;

	int k = 0;
	for(float i = 0; i < numVertRows; ++i)
	{
		for(float j = 0; j < numVertCols; ++j)
		{
			// Negate the depth coordinate to put in quadrant four.  
			// Then offset to center about coordinate system.
			verts[k].x =  j * dx + xOffset;
			verts[k].z = -i * dz + zOffset;
			verts[k].y =  0.0f;

			// Translate so that the center of the grid is at the
			// specified 'center' parameter.
			D3DXMATRIX T;
			D3DXMatrixTranslation(&T, center.x, center.y, center.z);
			D3DXVec3TransformCoord(&verts[k], &verts[k], &T);
			
			++k; // Next vertex
		}
	}

	//===========================================
	// Build indices.

	indices.resize(numTris * 3);
	 
	// Generate indices for each quad.
	k = 0;
	for(DWORD i = 0; i < (DWORD)numCellRows; ++i)
	{
		for(DWORD j = 0; j < (DWORD)numCellCols; ++j)
		{
			indices[k]     =   i   * numVertCols + j;
			indices[k + 1] =   i   * numVertCols + j + 1;
			indices[k + 2] = (i+1) * numVertCols + j;
					
			indices[k + 3] = (i+1) * numVertCols + j;
			indices[k + 4] =   i   * numVertCols + j + 1;
			indices[k + 5] = (i+1) * numVertCols + j + 1;

			// next quad
			k += 6;
		}
	}
}

void GSP320::buildBuffers()
{
	std::vector<D3DXVECTOR3> verts;
	std::vector<DWORD> indices;

	generateGrid(NUMROWS, NUMCOLS, XSPACING, ZSPACING, GRIDCENTER, verts, indices);

	// Save vertex count and triangle count for DrawIndexedPrimitive arguments.
	mNumVertices  = NUMROWS*NUMCOLS;
	mNumTriangles = (NUMROWS - 1) * (NUMCOLS - 1) * 2;

	// Obtain a pointer to a new vertex buffer.
	HR(gd3dDevice->CreateVertexBuffer(mNumVertices * sizeof(VertexPN), 
		D3DUSAGE_WRITEONLY,	0, D3DPOOL_MANAGED, &mVB, 0));

	// Now lock it to obtain a pointer to its internal data, and write the
	// grid's vertex data.
	VertexPN* v = 0;
	HR(mVB->Lock(0, 0, (void**)&v, 0));

	//added a normal to the grid so that it knows how to handle light
	for(DWORD i = 0; i < mNumVertices; ++i)
	{
		//setting position
		v[i].pos = verts[i];

		//since our grid is flat, the normal is simpley 0, 1, 0; It is pointing
		//straight up
		v[i].normal = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	}

	HR(mVB->Unlock());


	// Obtain a pointer to a new index buffer.
	//if we need too big of a grid we may need to change the D3DFMT to a 32 bit format
	//or draw the grid in pieces
	HR(gd3dDevice->CreateIndexBuffer(mNumTriangles*3*sizeof(WORD), D3DUSAGE_WRITEONLY,
		D3DFMT_INDEX16, D3DPOOL_MANAGED, &mIB, 0));

	// Now lock it to obtain a pointer to its internal data, and write the
	// grid's index data.

	WORD* k = 0;
	HR(mIB->Lock(0, 0, (void**)&k, 0));

	for(DWORD i = 0; i < mNumTriangles*3; ++i)
		k[i] = (WORD)indices[i];

	HR(mIB->Unlock());
}
